/*******************************************************************************
 * Copyright (c) 2011 EclipseDevX.org and others.
 * 
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0 which
 * accompanies this distribution, and is available at:
 * 
 *     http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/

package org.eclipsedevx.metrics;

import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.List;

import javancss.FunctionMetric;
import javancss.Javancss;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipsedevx.core.workspace.IWorkspaceChangeListener;
import org.eclipsedevx.metrics.model.MetricsData;

/**
 * 
 * 
 * @author dcarew
 */
public class MetricsChangeListener implements IWorkspaceChangeListener {

	public MetricsChangeListener() {
		
	}

    public void restore() {
        DevXMetricsPlugin.getPlugin().getProjectHistory();
    }
    
	public void handleChange(IProject project, IResourceDelta delta) throws CoreException {
	    if (delta.getKind() == IResourceDelta.REMOVED) {
	        DevXMetricsPlugin.getPlugin().getProjectHistory().removeProject(project);   
	    } else {
    	    boolean changed = processDeltas(delta.getAffectedChildren());
    	    
    	    if (changed) {
    	        MetricsData data = updateContainerMetrics(project);
    	        
    	        if (data != null) {
    	            DevXMetricsPlugin.getPlugin().getProjectHistory().updateMetrics(project, data);
    	        }
    	    }
	    }
	}

    public void save() {
        DevXMetricsPlugin.getPlugin().getProjectHistory().save();
    }

    private boolean processDeltas(IResourceDelta[] deltas) {
	    boolean changed = false;
	    
	    for (IResourceDelta delta : deltas) {
	        IResource resource = delta.getResource();
	        
            if (resource.isDerived()) {
                continue;
            }
            
	        int kind = delta.getKind();
	        
	        if (kind == IResourceDelta.ADDED || kind == IResourceDelta.CHANGED) {
	            if (resource instanceof IFile) {
	                if (processChanged((IFile)resource)) {
	                    changed = true;
	                }
	            } else if (resource instanceof IContainer) {
	                if (processDeltas(delta.getAffectedChildren())) {
	                    changed = true;
	                    
	                    updateContainerMetrics((IContainer)resource);
	                }
	            }
	        } else if (kind == IResourceDelta.REMOVED) {
	            changed = true;
	        }
	    }
	    
        return changed;
    }

    private MetricsData updateContainerMetrics(IContainer container) {
        try {
            MetricsData data = new MetricsData();
            
            for (IResource resource : container.members()) {
                MetricsData m = DevXMetricsPlugin.getPlugin().getMetricsData(resource);
                
                if (m != null) {
                    data.add(m);
                }
            }
            
            DevXMetricsPlugin.getPlugin().setMetricsData(container, data);
            
            return data;
        } catch (CoreException ce) {
            return null;
        }
    }

    private boolean processChanged(IFile file) {
        if ("java".equals(file.getFileExtension())) {
            MetricsData data = calcJavaMetrics(file);
            
            DevXMetricsPlugin.getPlugin().setMetricsData(file, data);
            
            return true;
        } else {
            return false;
        }
    }

    private MetricsData calcJavaMetrics(IFile file) {
        try {
            Javancss parser = null;
            
            try {
                Reader reader = new InputStreamReader(file.getContents(), file.getCharset());
                
                parser = new Javancss(reader); 
            } catch (UnsupportedEncodingException uee) {
                parser = new Javancss(file.getFullPath().toFile());
            }
            
            MetricsData data = new MetricsData();
            
            data.setValue(MetricsData.JAVA_FILES, 1);
            data.setValue(MetricsData.JAVA_SLOC, parser.getNcss());
            data.setValue(MetricsData.JAVA_CNC, calcCNC(parser.getFunctionMetrics()));
            
            return data;
        } catch (CoreException ce) {
            return null;
        }
    }

	public double calcCNC(List<?> list) {
		double cncSum = 0.0;
		double slocSum = 0.0;
		
		for (Object o : list) {
			FunctionMetric metric = (FunctionMetric)o;
			
			cncSum += (metric.ccn * metric.ncss);
			slocSum += metric.ncss;
		}
		
		if (cncSum == 0.0) {
			return 1.0;
		} else {
			return cncSum / slocSum;
		}
	}

}
